import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;

public class NonmetricMDS implements Runnable
{
	// global variables
	private Author[] authorsMap;
	private double[][] coordinates;
	private DBLPDatabase db;
	
	private double alpha = 1;
	private double beta = 1;
	private double gamma = 0.001;
	private double wij = 1;
	int x_min =0;
	int x_max =1000;
	int y_min =0; 
	int y_max =1000;
	int Num_threads = 1;
	int num_iterations = 1;
	int count_iterations = 0;
	Thread[] mdsThreads;
	ViewGraph v;
	//-------------------------------------------------
	public static void main(String args[])
	{
		new NonmetricMDS();
	}
	public NonmetricMDS()
	{
		
		try
		{
			db = new DBLPDatabase();
			int numberOfAuthors = db.getNumberOfAuthors();
			authorsMap = new Author[numberOfAuthors];
			coordinates = new double[numberOfAuthors][3];
			initializeCoordinates();;
			//loadCoordinates("MDS_2D_test1.txt");
			initializeAuthorsMap();
			loadMap("map.txt");
			mdsThreads = new Thread[Num_threads];
			long sTime = System.currentTimeMillis();
			ViewGraph view = new ViewGraph(coordinates,authorsMap);
			while(count_iterations<=num_iterations)
			{
				for(int i=0;i<mdsThreads.length;i++)
				{
					mdsThreads[i]= new Thread(this,Integer.toString(i));
					mdsThreads[i].start();
				}
				for(int i=0;i<mdsThreads.length;i++)
				{
					mdsThreads[i].join();
					//mdsThreads[i].destroy();
				}
				
				if(count_iterations%100 == 0) 
				{
					view.updatePaint(coordinates);
					//view.pointsPanel.saveImage(Integer.toString(count_iterations+1020));
				}
				count_iterations++;
				System.out.println("Iteration#: " + count_iterations);
			}
			
			long  fTime = System.currentTimeMillis();
			System.out.println("Time to run with " + Num_threads + " for " + 
					num_iterations + " is: " + (fTime-sTime)/1000 + " sec.");
			//saveCoordinates("MDS_2D_test2.txt");
			
			
		}
		catch(Exception ex)
		{
			System.out.println("Error 001:"+ ex.getMessage());
		}
	}
	//=========== load map ===========================
	public void loadMap(String fileName)
	{
		int count = 0;
		System.out.println(">Loading the map structure ... ");
		try
		{
			File file = new File(fileName);
			FileReader fileReader = new FileReader(file);
			BufferedReader buffer = new BufferedReader(fileReader);
			//boolean link = true;
			while(true)
			{
				String line = buffer.readLine();
				if(line == null)break;
				String[] splitNodes = line.split("-");
				int aID = Integer.parseInt(splitNodes[0].split(",")[0]);
				Author author = new Author(aID);
				//System.out.println("Author:" + splitNodes[0]);
				for(int i=1;i<splitNodes.length;i++)
				{
					int coID = Integer.parseInt(splitNodes[i].split(",")[0]);
					double weight = Double.parseDouble(splitNodes[i].split(",")[1]);
					author.addCoAuthor(coID,(int)weight);
					//System.out.println("\t couthor:" + coID);
				}
				authorsMap[aID-1] = author;
				count++;
			}
			//System.out.println(count);
			System.out.println(">Loading the map structure has been completed with ["+ count + "] authors");
		
		}
		catch(Exception ex)
		{
			System.out.println("Error 001:"+ex.getMessage());
		}
		 
	}
	//================================================
	public void initializeAuthorsMap()
	{
		for(int i=0;i<authorsMap.length;i++)
		{
			Author author = new Author(i+1);
			authorsMap[i] = author;
		}
	}
	//================================================
	private void initializeCoordinates()
	{
		System.out.println(">initializing authors coordinates ... randomlly");
		
		Random r = new Random();
		for(int i=0;i<coordinates.length;i++)
		{
			
			double x = r.nextDouble()*(x_max-x_min) + x_min;
			double y = r.nextDouble()*(y_max-y_min) + y_min;
			coordinates[i][0] = x;
			coordinates[i][1] = y;
			//System.out.println("Point [" + i+ "[ has coordinate of (" + x + "," + y + ")");
	
		}
		System.out.println(">["+ coordinates.length + "] coordinates have been completed");
	}
	//================================================
	public double getDisparities(int aID1,int aID2)
	{
		double f = 0.0;
		int weight = authorsMap[aID1-1].getWeight(aID2);
		if(weight>0)
		{
			f = alpha * Math.pow(weight, -1*beta);
		}
		return f;
	}
	//================================================
	public double getDistance(int aid1,int aid2)
	{
		double d = 0.0;
		// calc dij(x) = sqrt((xi2 - xj1)^2 + (xi2 - xj2)^2)
		double c1 = (coordinates[aid2-1][0] - coordinates[aid1-1][0]);
		double c2 = (coordinates[aid2-1][1] - coordinates[aid1-1][1]);
		double sum = Math.pow(c1,2) + Math.pow(c2,2);
		d = Math.sqrt(sum);
		return d;
	}
	//================================================
	public void run() 
	{
		int max = authorsMap.length;
		int div = (int) Math.ceil((double)max/Num_threads);
		//System.out.println(Thread.currentThread().getName());
		int tid = Integer.parseInt(Thread.currentThread().getName());
		int from = tid*div;
		int to = from + div -1;
		if(to>max) to = max-1;
		//System.out.println("Thread # " + tid + " on["+ from + "," + to + "]");
		double pdStress = calculatePartialDiffStress(from,to);
		
    }
	//================================================
	public double calculatePartialDiffStress(int from, int to)
	{
		double diff_sum_2 =0;
		double stress = 0;
		double sum_pStress = 0;
		for(int i=from;i<to;i++)
		{
			double pXi1 = 0;
			double pXi2 = 0;
			double pointStress = 0;
			//System.out.println(authorsMap[i].authorID);
			int aid1 = authorsMap[i].authorID;
			System.out.println("------------" + aid1);
			//System.out.println(authorsMap[i].getStringOfCo_Authors());
			Integer[] listOfCoauthorsID = authorsMap[i].getCoauthorsID();
			for(int j=0;j<listOfCoauthorsID.length;j++)
			{
				
				int aid2 = listOfCoauthorsID[j];
				//System.out.println(aid2);
				if(aid1!=aid2)
				{
					// sum [wij(fij - dij)^2]
					double equ = wij * Math.pow((getDisparities(aid1,aid2) - getDistance (aid1, aid2)),2);
					pointStress = pointStress + equ;
					diff_sum_2 = diff_sum_2 + pointStress;
					// sum[( fij - dij(x)) . dij(x)^-1 . (Xi1 - Xj1)
					double t1 = getDisparities(aid1,aid2) - getDistance (aid1, aid2);
					double t2 = 1/getDistance (aid1, aid2);
					double t31 = coordinates[aid1-1][0] - coordinates[aid2-1][0];
					double t32 = coordinates[aid1-1][1] - coordinates[aid2-1][1];
				
				
					pXi1 = pXi1 + t1*t2*t31;
					pXi2 = pXi2 + t1*t2*t32;
					
					//sum_pStress = sum_pStress + getDistance(aid1,aid2);
				}
				
			}// co-author loop
			//----test ----------------------------------------
			synchronized (this)
			{ 
				pXi1 = -2*pXi1;
				pXi2 = -2*pXi2;
				coordinates[aid1-1][0] = coordinates[aid1-1][0] - gamma * pXi1;
				coordinates[aid1-1][1] = coordinates[aid1-1][1] - gamma * pXi2;
				coordinates[aid1-1][2] = pointStress;
			}
			
			//-----------------------------------------------
		}// main author loop
		stress = diff_sum_2;
		System.out.println("Stress = " + stress);
		
		return stress;
	}
	//==================================================
	public double getPointStress(int pointID)
	{
		
		double diff_sum_2 =0;
		double pXi1 = 0;
		double pXi2 = 0;
		int aid1 = authorsMap[pointID].authorID;
		
		Integer[] listOfCoauthorsID = authorsMap[pointID].getCoauthorsID();
		
		for(int j=0;j<listOfCoauthorsID.length;j++)
		{
			
			int aid2 = listOfCoauthorsID[j];
			
			if(aid1!=aid2)
			{
				// sum [wij(fij - dij)^2]
				diff_sum_2 = diff_sum_2 + wij * Math.pow((getDisparities(aid1,aid2) - getDistance (aid1, aid2)),2);
				// sum[( fij - dij(x)) . dij(x)^-1 . (Xi1 - Xj1)
				double t1 = getDisparities(aid1,aid2) - getDistance (aid1, aid2);
				double t2 = 1/getDistance (aid1, aid2);
				double t31 = coordinates[aid1-1][0] - coordinates[aid2-1][0];
				double t32 = coordinates[aid1-1][1] - coordinates[aid2-1][1];
			
			
				pXi1 = pXi1 + t1*t2*t31;
				pXi2 = pXi2 + t1*t2*t32;
				
				//sum_pStress = sum_pStress + getDistance(aid1,aid2);
			}
		}
		return diff_sum_2;
	}
	//==================================================
	public void gradiantDescent()
	{
		for(int k=0;k<num_iterations;k++)
		{
			
			//v.updatePaint();
		}
	}
	//=======================================================
	//========================================================
	public void saveCoordinates(String fileName)
	{
		try
		{
			File file = new File(fileName);
			FileWriter fileWriter = new FileWriter(file,true);
			PrintWriter printWriter = new PrintWriter(fileWriter);
			for(int i=0;i< coordinates.length;i++)
			{
					printWriter.println(coordinates[i][0] + "," + coordinates[i][1]);
			}
			printWriter.close();
		}
		catch(IOException ex)
		{
			System.out.println(ex.getMessage());
		}
	}
	//=====================================================
	public void loadCoordinates(String fileName)
	{
		int count = 0;
		try
		{
			File file = new File(fileName);
			FileReader fileReader = new FileReader(file);
			BufferedReader buffer = new BufferedReader(fileReader);
			//boolean link = true;
			while(true)
			{
				String line = buffer.readLine();
				if(line == null)break;
				String[] splitNodes = line.split(",");
				coordinates[count][0] = Double.parseDouble(splitNodes[0].trim());
				coordinates[count][1] = Double.parseDouble(splitNodes[1].trim());
				count++;
				
			}
			//System.out.println(count);
			System.out.println(">Loading the map structure has been completed with ["+ count + "] authors");
		
		}
		catch(Exception ex)
		{
			System.out.println("Error 001:"+ex.getMessage());
		}
		 
	}
	//=========================================

}